package com.cocolove2.andbase;

import android.support.annotation.NonNull;
import android.text.TextUtils;

import com.cocolove2.andbase.bean.BaseResponse;
import com.cocolove2.andbase.bean.RefreshTokenBean;
import com.cocolove2.andbase.exception.CustomException;
import com.cocolove2.andbase.exception.TokenEmptyOrInvalidException;
import com.cocolover2.andbase.http.IResponseResult;
import com.cocolover2.andbase.http.OnHttpListener;
import com.cocolover2.andbase.http.api.ABaseApi;
import com.cocolover2.andbase.http.interceptor.LoggingInterceptor;
import com.cocolover2.andbase.utils.log.KLog;
import com.facebook.stetho.okhttp3.StethoInterceptor;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import io.reactivex.Observable;
import io.reactivex.functions.Function;
import okhttp3.OkHttpClient;
import okhttp3.ResponseBody;


/**
 * 自定义ApiHelper（使用单例模式）
 * (开发者可以根据自己项目的网络请求规则自行调整)
 * 这里是一个示例模版
 * Created by liubo on 10/1/16.
 */
@SuppressWarnings("all")
public class ApiHelper extends ABaseApi<ApiService> {
    //全局错误码
    public static final int CODE_INVALID_TOKEN = 1000005;//无效token错误码
    public static final int CODE_OFFLINE = 1000114;//帐号异地登录错误码
    //请求结果的判断码和信息的key
    public static final String KEY_CODE = "error_code";
    public static final String KEY_MESSAGE = "reason";

    public static final String IP = "http://api.nohttp.net";
    public static final String PROJECT_NAME = "/";
    public static final String BASE_URL = IP + PROJECT_NAME;


    private ApiHelper() {
        super(BASE_URL, ApiService.class);
    }

    public static ApiHelper getInstance() {
        return ApiHelperHolder.INSTANCE;
    }

    private static class ApiHelperHolder {
        private static final ApiHelper INSTANCE = new ApiHelper();
    }


    @NonNull
    @Override
    public OkHttpClient buildOkhttpClient() {
        OkHttpClient client = new OkHttpClient();
        OkHttpClient.Builder builder = client.newBuilder()
                .connectTimeout(10, TimeUnit.SECONDS)
                .writeTimeout(15, TimeUnit.SECONDS)
                .readTimeout(15, TimeUnit.SECONDS);

        builder.addInterceptor(new StethoInterceptor());
        builder.addInterceptor(new LoggingInterceptor());
        return builder.build();
    }


    /**
     * 定义请求码为什么时，请求判定为成功
     * 请求是成功的一般是0
     */
    @Override
    public boolean isResponseSuccess(int code) {
        return code == 0;
    }

    /**
     * 根据错误码处理自定义的异常(请求时成功的但是错误码不为0)
     *
     * @param code     错误码
     * @param msg      错误提示
     * @param listener
     */
    @Override
    protected void handleCustomizeException(int code, String msg, OnHttpListener listener) {
        KLog.e("这里是处理自定义请求异常的入口");
        if (code == CODE_OFFLINE) {//处理强制下线异常
//            ComApp.sendOfflineBroadcast("下线通知", msg);
        } else {
            //不写这句，自定义的错误回调不会向下传递（注意这里不同意http网络请求异常比如404）
            listener.onFailure(code, msg);
        }
    }

    /**
     * 处理请求异常，区别于自定义异常
     *
     * @param e        异常
     * @param listener 分发回调接口
     */
    @Override
    protected void handleErrorException(Throwable e, OnHttpListener listener) {
        KLog.e("这里是处理网络请求异常的入口-->" + e.getMessage());
        if (listener != null) {
            listener.onFailure(-1000, e.getMessage());
        }
    }

    /**
     * 获取错误码关键字(当请求返回{@link Observable<okhttp3.ResponseBody>}时使用)
     *
     * @return
     */
    @NonNull
    @Override
    protected String getErrCodeKey() {
        return KEY_CODE;
    }

    /**
     * 获取错误信息关键字(当请求返回{@link Observable<okhttp3.ResponseBody>}时使用)
     *
     * @return
     */
    @NonNull
    @Override
    protected String getErrMsgKey() {
        return KEY_MESSAGE;
    }


    /**
     * 定义哪些异常可以重新请求
     * <p>
     * 1.响应结果实现{@link IResponseResult}接口时必回调该方法<br>
     * 2.响应结果为{@link ResponseBody}时，返回数据为json且包含{@link #getErrCodeKey()}关键字才会回调该方法
     * </p>
     *
     * @param code 错误码
     * @return 返回 null 时不重新请求
     */
    @Override
    protected Exception retryWhenException(int code) {
        KLog.i("这里是定义哪些请求错误码需要抛出异常重试");
        if (code == CODE_INVALID_TOKEN) {
            return new TokenEmptyOrInvalidException(CODE_INVALID_TOKEN, "token 失效");
        }
        return null;
    }

    /**
     * 重试条件重置,参考{@link Observable#retryWhen(Function)}
     *
     * @param throwable 异常
     * @param tryCount  重试次数
     * @return
     */
    @Override
    protected Observable<?> retryWhenObservable(Throwable throwable, int tryCount) {
        //token失效后，重新获取token，并重新请求接口，重试2次
        if (throwable instanceof TokenEmptyOrInvalidException && tryCount <= 2) {
            return refreshToken();
        }
        return Observable.error(throwable);
    }

    /**
     * 刷新token
     * 示例代码仅供参考
     *
     * @return
     */
    private synchronized Observable<BaseResponse<RefreshTokenBean>> refreshToken() {
        Map<String, String> params = new HashMap<>();
        String account = DaoHelper.getInstance().getAccount();
        params.put("token", DaoHelper.getInstance().getToken());
        params.put("account", account);
        params.put("devicecode", "xxx");
        final Observable observable = getApiService().refreshToken(params);
        return observable.flatMap(new Function<BaseResponse<RefreshTokenBean>, Observable<?>>() {

            @Override
            public Observable<?> apply(BaseResponse<RefreshTokenBean> response) {
                if (response.getCode() == 0) {
                    if (response.getData() != null) {
                        if (!TextUtils.isEmpty(response.getData().getToken())) {
                            KLog.i("token", "刷新后的token=" + response.getData().getToken());
                            DaoHelper.getInstance().saveToken(response.getData().getToken());
                        } else {
//                            ComApp.sendOfflineBroadcast("提示", "令牌失效,请重新登录");
                        }
                    } else {
                        KLog.e("token", "刷新后的实体类为空");
                    }
                } else if (response.getCode() == CODE_INVALID_TOKEN) {//抛出异常，终止请求尝试
                    KLog.e("token", "token 刷新失败" + response.getCode());
                    return Observable.error(new CustomException(response.getCode(), response.getMessage()));
                }
                return observable;
            }
        });
    }


}
